using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Types;

namespace Storage
{
    using Exceptions;
    using EntityModels;

    #region StorageManager
   
    /// <summary>
    /// Rapresents the way in which the modules can handle and persist their own data.
    /// </summary>
    public sealed partial class StorageManager
    {
        #region Public methods

        #region GetMessageById

        /// <summary>
        /// Returns the message with the specified id.
        /// </summary>
        /// <param name="msgID">The message ID.</param>
        /// <returns>A message.</returns>
        public Message GetMessageById(int msgID)
        {
            try
            {
                var message = from m in Entities.Messages
                              where m.Id == msgID
                              select m;

                return message.First();
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgID);
            }
        }

        #endregion

        #region GetSentMessageById

        /// <summary>
        /// Returns the sent message with the specified id
        /// </summary>
        /// <param name="msgID">The message ID.</param>
        /// <returns>A sent message.</returns>
        public SentMessages GetSentMessageById(int msgID)
        {
            try
            {
                var message = from m in Entities.SentMessages
                              where m.Id == msgID
                              select m;

                return message.First();
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgID);
            }
        }

        #endregion

        #region GetAReceivedMessageById

        /// <summary>
        /// Returns the received message with the specified id.
        /// </summary>
        /// <param name="msgID">The message ID.</param>
        /// <returns>A received message.</returns>
        public ReceivedMessages GetAReceivedMessageById(int msgID)
        {
            try
            {
                var message = from m in Entities.ReceivedMessages
                              where m.Id == msgID
                              select m;

                return message.First();
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgID);
            }
        }

        #endregion

        #region GetAPageOfSentMessages

        /// <summary>
        /// Returns a range of messages sent by an user.
        /// </summary>
        /// <param name="username">The sender's username.</param>
        /// <param name="startIndex">The start index.</param>
        /// <param name="msgNumber">The number of messages to load.</param>
        /// <returns>A list of sent messages.</returns>
        public List<SentMessages> GetAPageOfSentMessages(string username, int startIndex, int msgNumber)
        {
            return Entities.SentMessages
                .Where(m => m.Sender.Username == username)
                .OrderBy(m => m.Message.Date).ThenBy(m => m.Message.Id)
                .Skip(startIndex)
                .Take(msgNumber)
                .ToList();
        }

        #endregion

        #region GetAPageOfReceivedMessages

        /// <summary>
        /// Returns a range of messages received by a user.
        /// </summary>
        /// <param name="username">The receiver's username.</param>
        /// <param name="startIndex">The start index.</param>
        /// <param name="msgNumber">The number of messages to load.</param>
        /// <returns>A list of received messages.</returns>
        public List<ReceivedMessages> GetAPageOfReceivedMessages(string username, int startIndex, int msgNumber)
        {
            return Entities.ReceivedMessages
                .Where(m => m.Receiver.Username == username)
                .OrderBy(m => m.Message.Date)
                .Skip(startIndex)
                .Take(msgNumber)
                .ToList();
        }

        #endregion

        #region GetAllSentMessagesByUsername

        /// <summary>
        /// Returns all messages sent by a user.
        /// </summary>
        /// <param name="username">The sender's username.</param>
        /// <returns>A list of sent messages.</returns>
        public List<SentMessages> GetAllSentMessagesByUsername(string username)
        {
            var user = GetUserByUserName(username);

            var result = from msg in Entities.SentMessages
                         where msg.Sender == user
                         orderby msg.Message.Date descending //ho tolto l'ordimento per id di mess orderby msg.Message.Date, msg.MessageId D.L. Gui 
                         select msg;

            return result.ToList();
        }

        #endregion

        #region GetAllReceivedMessagesByUsername

        /// <summary>
        /// Returns all messages received by a user.
        /// </summary>
        /// <param name="username">The receiver's username.</param>
        /// <returns>A list of received messages.</returns>
        public List<ReceivedMessages> GetAllReceivedMessagesByUsername(string username)
        {
            var user = GetUserByUserName(username);

            var result = from msg in Entities.ReceivedMessages
                         where msg.Receiver == user
                         orderby msg.Message.Date descending 
                         select msg;

            return result.ToList();
        }

        #endregion

        #region GetReceivedMessageByUsernameAndMsgId

        /// <summary>
        /// Returns one of the received messages of a user.
        /// </summary>
        /// <param name="username">The receiver's username.</param>
        /// <param name="msgID">The message Id.</param>
        /// <returns>The received message with the specified Id.</returns>
        public ReceivedMessages GetReceivedMessageByUsernameAndMsgId(string username, int msgID)
        {
            try
            {
                var result = from msg in GetAllReceivedMessagesByUsername(username)
                             where msg.MessageId == msgID
                             select msg;

                return result.First();
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgID);
            }
        }

        #endregion

        #region GetSentMessagesByUsernameAndMsgId

        /// <summary>
        /// Returns a list of SentMessages of an user. This list contains all the entry 
        /// of SentMessages sent by user with nickname "username" AND messageId = msgId
        /// (if a user sends the same message to N users, this list contains n entries)
        /// </summary>
        /// <param name="username">The user's username</param>
        /// <param name="msgID">The message Id.</param>
        /// <returns>The sent messages with the specified Id.</returns>
        public List<SentMessages> GetSentMessagesByUsernameAndMsgId(string username, int msgID)
        {
            try
            {
                var result = from msg in GetAllSentMessagesByUsername(username)
                             where msg.MessageId == msgID
                             select msg;

                return result.ToList();
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgID);
            }
        }

        #endregion

        #region DeleteReceivedMessage

        /// <summary>
        /// Delete one of the received message of an user
        /// </summary>
        /// <param name="username">The username</param>
        /// <param name="msgId">The msgId</param>
        public void DeleteReceivedMessage(string username, int msgId)
        {
            try
            {
                Entities.ReceivedMessages.DeleteOnSubmit(GetReceivedMessageByUsernameAndMsgId(username, msgId));
                Entities.SubmitChanges();

                tryRemoveMessage(msgId);
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgId);
            }
        }

        #endregion

        #region DeleteSentMessage

        /// <summary>
        /// Delete one of the sent message of a user.
        /// </summary>
        /// <param name="username">The user's username.</param>
        /// <param name="msgId">The message Id.</param>
        public void DeleteSentMessage(string username, int msgId)
        {
            try
            {
                Entities.SentMessages.DeleteAllOnSubmit(GetSentMessagesByUsernameAndMsgId(username, msgId));
                Entities.SubmitChanges();

                tryRemoveMessage(msgId);
            }
            catch (InvalidOperationException)
            {
                throw new MessageNotFoundException(msgId);
            }
        }

        #endregion

        #region DeleteAllReceivedMessages

        /// <summary>
        /// Deletes all received messages of a user.
        /// </summary>
        /// <param name="userName">The user's username.</param>
        public void DeleteAllReceivedMessages(string userName)
        {
            // Check if the user exists.
            User user = GetUserByUserName(userName);

            Entities.ReceivedMessages
                .Where(m => m.Receiver == user)
                .ForEach(m => DeleteReceivedMessage(userName, m.MessageId));
        }


        #endregion

        #region DeleteAllSentMessages

        /// <summary>
        /// Deletes all sent messages of a user.
        /// </summary>
        /// <param name="userName">The user's username.</param>
        public void DeleteAllSentMessages(string userName)
        {
            // Check if the user exists.
            User user = GetUserByUserName(userName);

            Entities.SentMessages
                .Where(m => m.Sender == user)
                .ForEach(m => DeleteSentMessage(userName, m.MessageId));
        }
        

        #endregion

        #endregion

        #region Private methods

        #region tryRemoveMessage

        /// <summary>
        /// Check if there are some references for the Message with Id = msgId.
        /// If no, deletes the entry of Messages with Id = msgId.
        /// </summary>
        /// <param name="msgId"></param>
        private void tryRemoveMessage(int msgId)
        {
            StorageManager.Instance.Entities.Message_tryDelete(msgId);
        }
    
        #endregion

        #endregion
    }
 
    #endregion
}
